﻿-- Author  : avina @ xjtu


function TetrisReset()
local Tetris = {

  key = {

-------  键位设置  -------
    LEFT = "left",
    RIGHT = "right",
    DOWN = "down",
    UP = "change",

    A = "left",
    D = "right",
    S = "down",
    W = "change",

    SPACE = "change",
--------------------------

  },
-------  闪烁速度  -------
  blinkspeed = 0.04,
--------------------------

-------  按键延迟  -------
  keydelay = 0.27,
--------------------------

----- 下边的不要改动 -----

  rival = "",
  show = true,
  mode = false,
  width = 250,
  height = 425,
  rows = 17,
  cols = 10,
  speed = 1,
  preview = true,
  cubes = {},
  cubes2 = {},
  currShape = nil,
  nextShape = nil,
  currType = 0,
  nextType = 0,
  currX = 0,
  currY = 0,
  nextX = 0,
  nextY = 0,
  lastHit = false,
  index = {17},
  fillOrFade = true,
  fillStep = 1,
  score = 0,
  honor = 0,
  pause = false,
  playing = false,
  gameOver = false,
  pvping = false,
}
return Tetris
end

Tetris = Tetris or TetrisReset()

local keyNow = nil
local startX, startY = 4, 0
local currPose = 1
local cubeTexture = "Interface\\TARGETINGFRAME\\UI-RaidTargetingIcon_6"
local cubeTexture2 = {
  "Interface\\ICONS\\Ability_Rogue_FeignDeath",
  "Interface\\ICONS\\INV_Stone_02",
  "Interface\\ICONS\\Spell_Holy_PainSupression",
  "Interface\\ICONS\\Ability_Warrior_ShieldWall",
  "Interface\\ICONS\\INV_Hammer_02",
}
local cubeTexture3 = {
  "Interface\\ICONS\\Spell_Shadow_RainOfFire",
  "Interface\\ICONS\\Ability_Mage_LivingBomb",
  "Interface\\ICONS\\Ability_Rogue_MurderSpree",
  "Interface\\ICONS\\Ability_Warrior_Bladestorm",
  "Interface\\ICONS\\Ability_Paladin_DivineStorm",
}
local s12icon = cubeTexture2[math.random(5)]
local s13icon = cubeTexture3[math.random(5)]

local PREFIX_DATA   = "t_data"
local PREFIX_DUEL   = "t_duel"
local PREFIX_ACCEPT   = "t_acc"
local PREFIX_DENY   = "t_deny"
local PREFIX_GOVER   = "t_gover"
local PREFIX_PVPING = "t_pvping"
local PREFIX_SURR = "t_surr"
local PREFIX_ERASE = "t_erase"

local popup = 0

-------------------- Draw --------------------

function TetrisCubesIni(f)
  local T, w, h, i, j = _G["TetrisDrawFrame"..f], Tetris.width / Tetris.cols, Tetris.height / Tetris.rows
  local dx, dy = 0, 0
  local b = T:CreateTexture("TetrisBackGround..f", "BACKGROUND")
  b:SetTexture(1, 1, 1, 0.1)
  b:ClearAllPoints()
  b:SetPoint("TOPLEFT", T, "TOPLEFT", dx, -dy)
  b:SetPoint("BOTTOMRIGHT", T, "TOPLEFT", 250 + dx, -425 - dy)
  local cubes = (f == 2 and Tetris.cubes2 or Tetris.cubes)
  for i = 1, Tetris.rows do
    cubes[i] = cubes[i] or {}
    for j = 1, Tetris.cols do
      local c = T:CreateTexture(f.."TetrisCube"..(i - 1) * Tetris.cols + j, "ARTWORK")
      cubes[i][j] = cubes[i][j] or {0}
      c:SetTexture(cubeTexture)
      c:SetVertexColor(1, 1, 1, 1)
      c:SetTexCoord(.05, .05, .05, .95, .95, .05, .95, .95)
      c:ClearAllPoints()
      c:SetPoint("TOPLEFT", T, "TOPLEFT", (j - 1) * w + 1.5 + dx, (1 - i) * h - dy - 1.5)
      c:SetPoint("BOTTOMRIGHT", T, "TOPLEFT", (j + 0) * w - 1.5 + dx, (0 - i) * h - dy + 1.5)
    end
  end
  cubes[0] = {{0,nil},{0,nil},{0,nil},{0,nil},{0,nil},{0,nil},{0,nil},{0,nil},{0,nil},{0,nil}}
  T, dx, dy, w, h = _G["TetrisFrame"..f], 270, 50, w * 0.3, h * 0.3
  for i = 1, 4 do
    cubes[i + Tetris.rows + 1] = Tetris.cubes[i + Tetris.rows + 1] or {}
    for j = 1, 4 do
      local c = T:CreateTexture(f.."TetrisCube"..(i + Tetris.rows) * Tetris.cols + j, "ARTWORK")
      cubes[i + Tetris.rows + 1][j] = Tetris.cubes[i + Tetris.rows + 1][j] or {0}
      c:SetTexture(cubeTexture)
      c:SetVertexColor(1, 1, 1, 1)
      c:SetTexCoord(.05, .05, .05, .95, .95, .05, .95, .95)
      c:ClearAllPoints()
      c:SetPoint("TOPLEFT", T, "TOPLEFT", (j - 1) * w + 0.5 + dx, (1 - i) * h - dy - 0.5)
      c:SetPoint("BOTTOMRIGHT", T, "TOPLEFT", (j + 0) * w - 0.5 + dx, (0 - i) * h - dy + 0.5)
    end
  end
  for i = 1, 15 do
    local c = T:CreateTexture(f.."TetrisCube"..1000 + i, "ARTWORK")
    c:SetTexture("Interface\\PvPRankBadges\\PvPRank"..(i > 9 and "" or "0")..i)
    c:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)
    c:SetVertexColor(1, 1, 1, 1)
    c:ClearAllPoints()
    c:SetPoint("TOPLEFT", T, "TOPLEFT", 275, (i - 18.4) * 25)
    c:SetPoint("BOTTOMRIGHT", T, "TOPLEFT", 295, (i - 18.4) * 25 - 20)
    c:Hide()
  end
end

function TetrisDrawCube(f)
  local i, j
  local cubes = (f == 2 and Tetris.cubes2 or Tetris.cubes)
  for i = 1, Tetris.rows + 5 do
    for j = 1, Tetris.cols do
      if cubes[i] and cubes[i][j] then
        local c = _G[f.."TetrisCube"..(i - 1) * Tetris.cols + j]
        if c then
          if cubes[i][j][1] ~= 0 then
            if Tetris.currType == 12 and cubes[i][j][1] == 3 and i <= Tetris.rows then
              c:SetTexture(s12icon)
            elseif Tetris.currType == 13 and cubes[i][j][1] == 3 and i <= Tetris.rows then
              c:SetTexture(s13icon)
            else
              c:SetTexture(cubeTexture)
            end
            if i > Tetris.rows then if Tetris.preview then c:Show() else c:Hide() end else c:Show() end
          else
            c:Hide()
          end
        end
      end
    end
  end
end

function TetrisPreviewClear()
  local i, j
  for i = 1, 4 do
    for j = 1, 4 do
      Tetris.cubes[i + Tetris.rows + 1][j][1] = 0
    end
  end
end

-------------------- Actions --------------------

function TetrisShapeRandom()
  local t = Tetris.mode and math.random(13) or math.random(7)
  Tetris.nextType = t
  currPose = 1
  if t == 1 then
    return {{0, 1}, {1, 1}, {2, 1}, {1, 2}}
  elseif t == 2 then
    return {{1, 0}, {1, 1}, {1, 2}, {2, 2}}
  elseif t == 3 then
    return {{1, 0}, {1, 1}, {1, 2}, {0, 2}}
  elseif t == 4 then
    return {{0, 1}, {1, 1}, {1, 0}, {2, 0}}
  elseif t == 5 then
    return {{0, 0}, {1, 0}, {1, 1}, {2, 1}}
  elseif t == 6 then
    return {{0, 1}, {1, 1}, {2, 1}, {3, 1}}
  elseif t == 7 then
    return {{0, 0}, {0, 1}, {1, 0}, {1, 1}}
  elseif t == 8 then
    return {{0, 0}, {0, 1}, {1, 0}}
  elseif t == 9 then
    return {{0, 0}, {1, 0}, {2, 0}, {1, 1}, {1, 2}}
  elseif t == 10 then
    return {{0, 1}, {1, 1}, {2, 1}, {1, 0}, {1, 2}}
  elseif t == 11 then
    return {{0, 0}, {0, 1}, {1, 1}, {2, 1}, {2, 0}}
  elseif t == 12 or t == 13 then
    s12icon = cubeTexture2[math.random(5)]
    s13icon = cubeTexture3[math.random(5)]
    return {{0, 0}}
  end
end

function TetrisShapeNew(x, y)
  local s, i = TetrisShapeRandom()
  TetrisPreviewClear()
  for i = 1, 5 do
    if s[i] then
      Tetris.cubes[s[i][2] + Tetris.rows + y + 2][s[i][1] + x - 3][1] = 3
    end
  end
  Tetris.currX, Tetris.currY = x, y
  Tetris.nextX, Tetris.nextY = Tetris.currX, Tetris.currY
  return s
end

function TetrisShapeMove(key)
  if not Tetris.playing then return end
  key = Tetris.key[key]
  if not key or not Tetris.currShape or Tetris.pause then return false end
  local r, i, p1 = true
  for i = 1, 5 do
    if Tetris.currShape[i] then
      p1 = Tetris.cubes[Tetris.currY + Tetris.currShape[i][2]][Tetris.currX + Tetris.currShape[i][1]]
      if Tetris.lastHit == 4 then p1[1] = 1 else p1[1] = 0 end
    end
  end
  if key == "change" then
    if Tetris.currType == 4 then
      TetrisShapeRotate(1, 1, currPose)
    elseif Tetris.currType == 8 then
      TetrisShapeRotate(0.5, 0.5, 1)
    elseif Tetris.currType == 12 then
      TetrisShapeLand()
    elseif Tetris.currType == 13 then
      TetrisExplode()
      return true
    else
      TetrisShapeRotate(1, 1, 1)
    end
  elseif key == "left" then
    Tetris.nextX = Tetris.nextX - 1
  elseif key == "right" then
    Tetris.nextX = Tetris.nextX + 1
  elseif key == "down" then
    Tetris.nextY = Tetris.nextY + 1
  end
  hit = TetrisShapeHit(Tetris.nextX, Tetris.nextY)
  Tetris.lastHit = hit
  if hit and hit ~= 4 then
    if key == "change" then
      TetrisShapeRotate(1, 1, -1)
    elseif key == "down" and (hit == 1 or hit == 3) then
      TetrisShapeLand()
      return false
    else
      Tetris.nextX, Tetris.nextY = Tetris.currX, Tetris.currY
    end
    r = false
  else
    Tetris.currX, Tetris.currY = Tetris.nextX, Tetris.nextY
  end
  for i = 1, 5 do
    if Tetris.currShape and Tetris.currShape[i] then
      Tetris.cubes[Tetris.nextY + Tetris.currShape[i][2]][Tetris.nextX + Tetris.currShape[i][1]][1] = 3
    end
  end
  return r
end

function TetrisExplode()
  local i, j
  for i = Tetris.currY - 1, Tetris.currY + 1 do
    for j = Tetris.currX - 1, Tetris.currX + 1 do
      if i <= Tetris.rows and Tetris.cubes[i][j] then Tetris.cubes[i][j][1] = 0 end
    end
  end
  Tetris.lastHit = false
  TetrisEraseLine()
  if not TetrisOverTop() then
    TetrisStart()
  else
    Tetris.gameOver = true
    TetrisGameOver(false)
    for i = 1, Tetris.cols do
      Tetris.cubes[0][i][1] = 0
    end
    Tetris.currShape = nil
  end
end

function TetrisShapeRotate(ox, oy, d) -- d : direction  1 / -1
  if Tetris.currType == 7 or Tetris.currType == 10 then return end
  local i, x, y
  if Tetris.currType > 3 and Tetris.currType < 7 then
    d = -currPose
    currPose = d
  end
  for i = 1, 5 do
    if Tetris.currShape[i] then
      x, y = Tetris.currShape[i][1], Tetris.currShape[i][2]
      x, y = (x - ox) * d, (oy - y) * d
      x, y = y, x
      x, y = x + oy, y + ox
      Tetris.currShape[i][1], Tetris.currShape[i][2] = x, y
    end
  end
end

function TetrisShapeFall()
  TetrisTimer(TetrisFrame,Tetris.speed, TetrisShapeMove, "DOWN")
end

function TetrisShapeLand()
  for i = 1, 5 do
    if Tetris.currShape[i] then
      Tetris.cubes[Tetris.currY + Tetris.currShape[i][2]][Tetris.currX + Tetris.currShape[i][1]][1] = 1
    end
  end
  TetrisEraseLine()
  if not TetrisOverTop() then
    TetrisStart()
  else
    Tetris.gameOver = true
    TetrisGameOver(false)
    for i = 1, Tetris.cols do
      Tetris.cubes[0][i][1] = 0
    end
    Tetris.currShape = nil
  end
end

function TetrisShapeHit(x, y)    --  1 bottom, 2 wall, 3 other shape, 4 pass
  if not Tetris.currShape then return end
  local i
  for i = 1, 5 do
    if Tetris.currShape[i] then
      if not Tetris.cubes[y + Tetris.currShape[i][2]] then
        return 1
      elseif not Tetris.cubes[y + Tetris.currShape[i][2]][x + Tetris.currShape[i][1]] then
        return 2
      elseif Tetris.cubes[y + Tetris.currShape[i][2]][x + Tetris.currShape[i][1]][1] == 1 then
        if Tetris.currType == 12 or Tetris.currType == 13 then return 4 else return 3 end
      end
    end
  end
  return false
end

function TetrisPlay()
  Tetris.score, Tetris.honor = 0, 0
  TetrisFSScore:SetText("Gold: 0 g")
  TetrisFSHonor:SetText("Honor: 0")
  for i = 1, 15 do
    local c = _G["TetrisCube"..1000 + i]
    c:Hide()
  end
  Tetris.playing = true
  TetrisSwitch(false)
  TetrisStart()
end

function TetrisStart()
  Tetris.currShape, Tetris.currType = Tetris.nextShape, Tetris.nextType
  for i = 1, 5 do
    if Tetris.currShape[i] then
      Tetris.cubes[0 + Tetris.currShape[i][2]][4 + Tetris.currShape[i][1]][1] = 3
    end
  end
  Tetris.nextShape = TetrisShapeNew(startX, startY)
  TetrisShapeFall()
end

function TetrisPause()
  Tetris.pause = not Tetris.pause
  TetrisFrame.start = not Tetris.pause
  if Tetris.pause then TetrisFSPause:Show() else TetrisFSPause:Hide() end
end

function TetrisEraseLine()
  local n, i, j, l, k = 0
  for i = 1, Tetris.rows do
    l = 1
    for j = 1, Tetris.cols do
      l = l * Tetris.cubes[i][j][1]
    end
    if l ~= 0 then
      for l = i, 1, -1 do
        for k = 1, Tetris.cols do
          Tetris.cubes[l][k][1] = Tetris.cubes[l - 1][k][1]
        end
      end
      Tetris.score = Tetris.score + 100
      n = n + 1
      TetrisFSScore:SetText("Gold: "..Tetris.score.." g")
      TetrisShowBG()
    end
  end
  if n > 1 then Tetris.honor = Tetris.honor + (Tetris.mode and 300 or 100) * 2^(n - 1) end
  if n > 0 and Tetris.pvping then
    if n == 4 then
      SendAddonMessage(PREFIX_ERASE, 3, "WHISPER", Tetris.rival)
      popup = 0
      return
    else
      popup = popup + n
      SendAddonMessage(PREFIX_ERASE, math.floor(popup/2), "WHISPER", Tetris.rival)
      popup = popup - 2 * math.floor(popup/2)
    end
  end
  TetrisFSHonor:SetText("Honor: "..Tetris.honor)
  TetrisShowPVP()
end

function TetrisShowBG()
  if Tetris.score % 12000 >= 10000 then
    TetrisDrawFrameBG:SetTexture("Interface\\GLUES\\CREDITS\\BladesEdgeMountains5")
  elseif Tetris.score % 12000 >= 8000 then
    TetrisDrawFrameBG:SetTexture("Interface\\TALENTFRAME\\WarlockDestruction-TopLeft")
  elseif Tetris.score % 12000 >= 6000 then
    TetrisDrawFrameBG:SetTexture("Interface\\TALENTFRAME\\PriestDiscipline-TopLeft")
  elseif Tetris.score % 12000 >= 4000 then
    TetrisDrawFrameBG:SetTexture("Interface\\TALENTFRAME\\PALADINCOMBAT-TOPLEFT")
  elseif Tetris.score % 12000 >= 2000 then
    TetrisDrawFrameBG:SetTexture("Interface\\TALENTFRAME\\PriestHoly-TopLeft")
  elseif Tetris.score % 12000 >= 0 and Tetris.score > 11000 then
    TetrisDrawFrameBG:SetTexture("Interface\\GLUES\\CREDITS\\Titan13")
  end
end

function TetrisShowPVP()
  for i = 1, 15 do
    local c = _G["TetrisCube"..1000 + i]
    if i <= floor(Tetris.honor^0.5 / 20) then
      c:Show()
    end
  end
end

function TetrisOverTop()
  local i
  for i = 1, Tetris.cols do
    if Tetris.cubes[0][i][1] == 1 then
      TetrisFrame.start = false
      return true
    end
  end
  return false
end

function TetrisGameOver(win)
  TetrisFSGameOver:Show()
  Tetris.playing = false
  TetrisTimer(TetrisFrame, 0.05, TetrisFillUp, Tetris.index)
  if Tetris.pvping and Tetris.rival then
    if win then
      Tetris.pvping = false;
	    TetrisDuelCancel()
    else
      SendAddonMessage(PREFIX_GOVER, "", "WHISPER", Tetris.rival)
      TetrisDuelLose()
    end
  end
end

function TetrisFillUp(i)
  i = i[1]
  if i < 1 and Tetris.fillOrFade then
    Tetris.fillOrFade, Tetris.fillStep, i = false, -1, 1
  end
  if i > Tetris.rows and not Tetris.fillOrFade then
    Tetris.fillOrFade, TetrisFrame.start, Tetris.index, Tetris.fillStep  = true, false, {Tetris.rows}, 1
    TetrisFSGameOver:Hide()
    TetrisSwitch(true)
    Tetris.gameOver = false
    return
  end
  local j
  for j = 1, Tetris.cols do
    Tetris.cubes[i][j][1] = 1 + Tetris.fillStep
  end
  Tetris.index[1] = i - Tetris.fillStep
end

function TetrisSwitch(b)
  if b then
    TetrisFramePlayButton:Enable()
    TetrisFrameSpeed:Enable()
    TetrisFrameExtra:Enable()
    TetrisFramePreview:Enable()
    TetrisFramePVPButton:Enable()
    TetrisFramePauseButton:Disable()
  else
    TetrisFramePlayButton:Disable()
    TetrisFrameSpeed:Disable()
    TetrisFrameExtra:Disable()
    TetrisFramePreview:Disable()
    TetrisFramePVPButton:Disable()
    TetrisFramePauseButton:Enable()
    TetrisFramePVPButton:Disable()
  end
end

-------------------- Events --------------------

function TetrisFrameOnLoad(self)
  self:RegisterEvent("VARIABLES_LOADED")
  self:RegisterEvent("CHAT_MSG_ADDON")
  self.counter, self.time, self.start, self.cbf, self.cbfarg = 0, 0, false
end


function TetrisDrawFrameOnLoad(self)
  self.counter, self.time, self.start, self.cbf, self.cbfarg = 0, 0, false
end


function TetrisFrameOnEvent(self, event, ...)
  if event == "VARIABLES_LOADED" then

	--兼容性
	if Tetris.keydelay == nil then Tetris = TetrisReset() end


    TetrisCubesIni("")
    TetrisCubesIni(2)
    Tetris.nextShape = Tetris.nextShape or TetrisShapeNew(startX, startY)
    TetrisFrame.start = TetrisFrame.start or false
    TetrisFramePreview:SetChecked(Tetris.preview)
    TetrisFrameExtra:SetChecked(Tetris.mode)
    TetrisFrameSpeed:SetValue(10 / Tetris.speed)
    TetrisDrawFrameBG:SetAlpha(0.16)
    if Tetris.show then
      self:Show()
    end
    TetrisFSScore:SetText("Gold: "..Tetris.score.." g")
    TetrisFSHonor:SetText("Honor: "..Tetris.honor)
    TetrisShowPVP()
    TetrisShowBG()
    TetrisSwitch(not Tetris.playing)
    if Tetris.pause then TetrisFSPause:Show() else TetrisFSPause:Hide() end
    if Tetris.gameOver then
      if Tetris.pause then TetrisPause() end
      TetrisGameOver(false)
    elseif Tetris.playing then
      TetrisShapeFall()
    end
  elseif event == "CHAT_MSG_ADDON" then
		local prefix, msg, channel, sender = select(1,...)
		--if sender == UnitName("player") then return end
		if prefix == PREFIX_DATA then
			TetrisNumToCubes(Tetris_rev_2(msg))
		elseif prefix == PREFIX_DUEL then
		  print(sender.."向你发起一次俄罗斯方块决斗")
		  if Tetris.playing then
		    SendAddonMessage(PREFIX_PVPING, "", "WHISPER", sender)
		    return
		   end
			if sender == UnitName("player") or  not Tetris.pvping then
			  Tetris.pvping = true
		    TetrisPVPConfirm(sender)
		  else
		    SendAddonMessage(PREFIX_PVPING, "", "WHISPER", sender)
		  end
		elseif prefix == PREFIX_ACCEPT then
			if sender == UnitName("player") then return end
			TetrisDuelStart()
		elseif prefix == PREFIX_DENY then
			TetrisFSPVPMsg:SetText(Tetris.rival.."  拒绝了你的决斗 T_T")
			TetrisPVPMsgFrame:Show()
			Tetris.rival = nil
			TetrisDuelCancel()
		elseif prefix == PREFIX_GOVER then
		  if sender == UnitName("player") then return end
		  TetrisDuelWin()
		elseif prefix == PREFIX_PVPING then
		  TetrisFSPVPMsg:SetText(Tetris.rival.."  正在决斗中")
			TetrisPVPMsgFrame:Show()
			Tetris.rival = nil
			TetrisDuelCancel()
	  elseif prefix == PREFIX_SURR then
	    if Tetris.playing then
	      if sender == UnitName("player") then return end
		    TetrisDuelWin()
	    else
	      TetrisPVPConfirmFrame:Hide()
	      Tetris.rival = nil
        TetrisDuelCancel()
	    end
	  elseif prefix == PREFIX_ERASE then
	    TetrisPopup(msg)
		end
  end
end

----------- new key event funtions -----------

function TetrisFrameOnKeyDown(self, key)
  if key == "ESCAPE" then
    TetrisFrameHide()
  elseif key == "P" then
    if Tetris.playing then
      TetrisPause()
     else
      TetrisPlay()
     end
  else
  	keyNow = key
  	TetrisShapeMove(key)               -- move immediately
		TetrisTimerOnceStart(Tetris.keydelay, key)    -- delay before blink
	end
end

function TetrisFrameOnKeyUp(self, key)
  if keyNow == key then
	  TetrisDrawFrame.start = false
  end
  --if key == "Q" then TetrisFrame.start = false end
end

function TetrisTimerOnceStart(t, key)
	TetrisTimer(TetrisDrawFrame, t, TetrisTimerOnceEnd, key)
end

function TetrisTimerOnceEnd(key)
  TetrisTimer(TetrisDrawFrame, Tetris.blinkspeed, TetrisShapeMove, key)
end

----------------------------------------------

function TetrisFrameHide()
  TetrisFrame:Hide()
  TetrisDrawFrame:Hide()
  Tetris.show = false
end

function TetrisFrameShow()
  TetrisFrame:Show()
  TetrisDrawFrame:Show()
  Tetris.show = true
end

function TetrisDrawFrameOnUpdate(self, elapsed)
  TetrisDrawCube("")
  if self.start and self.time > 0 then
    local c, t = self.counter, self.time
    c = c + elapsed
    if c > t then c = 0 self.cbf(self.cbfarg) end
    self.counter = c
  end

end

-- old key event function
--function TetrisFrameOnChar(self, text)
	--if text == " " then text = "space" end
  --text = string.upper(text)
  --if not text then text = "" end
  --TetrisShapeMove(text)
--end

function TetrisFramePreviewOnClick(self)
  Tetris.preview = self:GetChecked()
end

function TetrisFrameExtraOnClick(self)
  Tetris.mode = self:GetChecked()
end

function TetrisFrameSpeedOnVC(v)
  Tetris.speed = 10 / v
end

-------------------- PVP --------------------

function TetrisPVPIni()
  if not Tetris.pvping then
  	Tetris.pvping = true
  	TetrisSwitch(false)
  	TetrisFramePauseButton:Disable()
  	TetrisFrameCloseButton:Disable()
	  TetrisPVPIniFrame:Show()
  end
end

function TetrisDuelSend(t)
  TetrisPVPIniFrame:Hide()
  TetrisFrame2:Show()
  Tetris.rival = t
  SendAddonMessage(PREFIX_DUEL, "", "WHISPER", t)
end

function TetrisDuelCancel()
  TetrisPVPIniFrame:Hide()
  TetrisFrame2:Hide()
  TetrisSwitch(true)
  TetrisFrameCloseButton:Enable()
  Tetris.pvping = false
end

function TetrisPVPConfirm(sender)
  TetrisPVPIniFrame:Hide()
  TetrisFrame2:Show()
  Tetris.rival = sender
  TetrisSwitch(false)
  TetrisFramePauseButton:Disable()
  TetrisFrameCloseButton:Disable()
  TetrisDuelSurrButton:Disable()
  TetrisFSDuelFrom:SetText(sender.."\n发起一次俄罗斯方块决斗！")
  TetrisPVPConfirmFrame:Show()
end

function TetrisDuelAccept()
  if Tetris.playing then
    TetrisCubesClear()
  end
  SendAddonMessage(PREFIX_ACCEPT, "", "WHISPER", Tetris.rival)
  TetrisDuelSurrButton:Enable()
  TetrisDuelStart()
end

function TetrisDuelStart()
  TetrisSendSyncStart()
	Tetris.score, Tetris.honor = 0, 0
  TetrisFSScore:SetText("Gold: 0 g")
  TetrisFSHonor:SetText("Honor: 0")
  for i = 1, 15 do
    local c = _G["TetrisCube"..1000 + i]
    c:Hide()
  end
  TetrisFrame2RivalName:SetText(Tetris.rival)
  Tetris.playing = true
  Tetris.pvping = true
  TetrisStart()
end

function TetrisDuelDeny()
	SendAddonMessage(PREFIX_DENY, "", "WHISPER", Tetris.rival)
	if Tetris.rival == UnitName("player") then return end
  Tetris.rival = nil
  TetrisDuelCancel()
end

function TetrisDuelWin()
  Tetris.gameOver = true
  TetrisGameOver(true)
  TetrisFSPVPMsg:SetText("你击败了"..Tetris.rival.."！")
	TetrisPVPMsgFrame:Show()
end

function TetrisDuelLose()
  TetrisFSPVPMsg:SetText("你输给了"..Tetris.rival.." T_T")
	TetrisPVPMsgFrame:Show()
	Tetris.pvping = false;
	TetrisDuelCancel()
end

function TetrisDuelSurr()
  if Tetris.pvping then
    SendAddonMessage(PREFIX_SURR, "", "WHISPER", Tetris.rival)
    if Tetris.playing then
      Tetris.gameOver = true
      TetrisGameOver(false)
      TetrisDuelLose()
    else
      TetrisFSPVPMsg:SetText("你取消了与 "..Tetris.rival.." 的决斗")
	    TetrisPVPMsgFrame:Show()
	    Tetris.pvping = false;
	    TetrisDuelCancel()
    end
  end
end

function TetrisPopup(n)
  local i, j, k
  for k = 1, n do
    local cubes = Tetris.cubes
    for i = 1, Tetris.rows do
      for j = 1, Tetris.cols do
        if cubes[i] and cubes[i][j] then
          if i == Tetris.rows then
            cubes[i][j][1] = 1
          else
            if cubes[i+1][j][1] == 3 or cubes[i][j][1] == 3 then

            else
              cubes[i][j][1] = cubes[i+1][j][1]
            end
          end
        end
      end
    end
    for j = 1,math.random(2,5) do
      cubes[Tetris.rows][math.random(1,10)][1] = 0
    end
  end
end

function TetrisCubesClear()
  Tetris.playing = false
  Tetris.pause = false
  TetrisSwitch(true)
  local i, j
  for i=1, Tetris.rows do
    for j = 1, Tetris.cols do
      Tetris.cubes[i][j][1] = 0
    end
  end
end


-------------------- Timer --------------------

function TetrisTimer(f, time, cbf, cbfarg)
  f.time = time
  f.cbf = cbf
  f.cbfarg = cbfarg
  f.start = true
  f.counter = 0
end

function TetrisFrameOnUpdate(self, elapsed)
  if self.start and self.time > 0 then
    local c, t = self.counter, self.time
    c = c + elapsed
    if c > t then c = 0 self.cbf(self.cbfarg) end
    self.counter = c
  end
end

function TetrisDrawFrame2OnUpdate(self, elapsed)
  TetrisDrawCube(2)
  if self.start and self.time > 0 then
    local c, t = self.counter, self.time
    c = c + elapsed;
    if c > t then c = 0 self.cbf(self.cbfarg) end
    self.counter = c
  end
end


-------------------- Slash --------------------

SlashCmdList["TETRIS"] = function(txt)
         TetrisFrameShow()
         Tetris.show = true
         if txt == "reset" then
	         TetrisFramePlayButton:Disable()
	         TetrisFramePauseButton:Disable()
	         Tetris = nil
	         ReloadUI()
         end
 end
 SLASH_TETRIS1 = "/tetris"


-------------------- Sync --------------------

local Tetris_64 = {"1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k",
"l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G",
"H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","@","#"}
Tetris_64[0] = "0"

function TetrisSendSyncStart()
  TetrisTimer(TetrisDrawFrame2, 0.06, Tetris_Sync, nil)
end

function TetrisSendSyncStop()
  TetrisDrawFrame2.start = false
end

function Tetris_Sync(f)
	SendAddonMessage(PREFIX_DATA, TetrisCubesToNum(), "WHISPER", Tetris.rival)
end

function TetrisCubesToNum()
	local cubes, d = Tetris.cubes, {}
  for i = 1, Tetris.rows do
  	local b = ""
    for j = 1, Tetris.cols do
      if cubes[i] and cubes[i][j] then
        b = b..(cubes[i][j][1] > 0 and 1 or 0)
      end
    end
    d[i] = tonumber(b,2)
  end
  return Tetris_trans(d, Tetris.rows)
end

function TetrisNumToCubes(n)
	local cubes, b = Tetris.cubes2
  for i = 1, Tetris.rows do
  	b = Tetris_b_10(Tetris_d2b_str(n[i]))
    for j = 1, Tetris.cols do
      cubes[i][j][1] = string.sub(b,j,j) == "1" and 1 or 0
    end
  end
end

-------------------- number format --------------------

function Tetris_trans(v, n)
	local s, i = ""
	for i=1, n do
		s = s..( v[i] < 64 and "0"..Tetris_64[v[i]] or Tetris_d264_str(v[i]) )
	end
	return s
end

function Tetris_rev(s)  -- 1 64num
	local n, l, i = {}, strlen(s)
	for i=1, l do
		n[i] = Tetris_642d_num(strsub(s,i,i))
	end
	return n
end

function Tetris_rev_2(s) -- 2 64num
	local n, v, l, i = {}, Tetris_rev(s), strlen(s)/2
	for i=1, l do
		n[i] = v[i*2-1]*64 + v[i*2]
	end
	return n
end

function Tetris_10(x)
	local i
	for i=0,63 do
		if Tetris_64[i] == x then return i end
	end
	return nil
end

function Tetris_d264_str(n)
	if not n then return nil end
	n = tonumber(n)
	local x = ""
	repeat
		local m = Tetris_64[mod(n, 64)]
		n = floor(n / 64)
		x = m..x
	until n == 0
	return x
end

function Tetris_642d_num(x)
	if not x or x == "" then return nil end
	local l, d, i = strlen(x), 0
	for i = 1, l do
		d = d + Tetris_10(strsub(x, i, i)) * 64^(l-i)
	end
	return d
end

function Tetris_d2b_str(n)
	if not n then return nil end
	n = tonumber(n)
	local b = ""
	repeat
		local m = mod(n, 2)
		n = floor(n / 2)
		b = m..b
	until n == 0
	return b
end

function Tetris_b_10(b)
	local x = 10 - strlen(b)
	for i = 1, x do
		b = "0"..b
	end
	return b
end
